﻿// MathGame.cpp : 定义应用程序的入口点。
//

#include <Windows.h>
#include "framework.h"
#include "MathGame.h"
#include <time.h>
#include <stdio.h>
#include <gdiplus.h>
#pragma comment (lib,"Gdiplus.lib")
using namespace Gdiplus;
#include "MyTimer.h"
//#include "MyPaintUnit.h"

#define MAX_LOADSTRING 100
//测试的注释2
//可调整参数：界面大小、数字数量、颜色、随机移动数字次数
//界面大小
#define OriginalClientMainRCcx 510//默认总窗口RECTcx
#define OriginalClientMainRCcy 480//总
#define R RelSize
//数字数量等，横纵数字总量最好不超过100，请勿将横纵都调为1
#define MMxNum 4//横向格子数
#define MMyNum 4//纵向格子数
#define NullMath MMxNum*MMyNum//可自由移动的空白格子所对应的位置
//部分颜色
#define Col_NumBkgColFloat Color(200/MMxNum,0,200/MMyNum)//渐变
#define Col_FontWin RGB(200,190,120)
#define Col_FontMenu RGB(255,255,160)
#define Col_FontTimer RGB(240,180,230)
#define Col_FontRecord Col_Timer
#define Col_FontNum RGB(20,20,20)
#define Col_FontNumPen RGB(50,50,50)
//计时器
#define InvalidateRectTimer 1//刷新
#define GetMouseInRCTimer 2//获取鼠标位置
#define DelayInvalidateTimer 3//延迟刷新
#define TimerNum 3//计时器数量
//其他
#define MAXEscSec 0xffff//可记录的最多时间
#define RandMathTimes 4*MMxNum*MMxNum*MMyNum*MMyNum//开始时随机移动数字次数，如果数字总量较大建议调高
#ifndef RandMathTimes//若想手动设置请把上面的注释掉，在这里改
    #define RandMathTimes 2
#endif // !RandMathTimes
#define NullNum NullMath
#define MMSpace ((14-MMxNum/2-MMyNum/2)*R)//空格大小，可自定义
//#define My_SetRect(aRect,left,top,width,height) (aRect.X=left;aRect.Y=top;aRect.Width=width;aRect.Height=height;)//为Rect赋值
//#define My_SetRect(aRect,left,top,width,height) (SetRect(&(aRC),(left)*RelSize,(top)*RelSize,(right)*RelSize,(bottom)*RelSize))//为Rect赋值时乘上R
#define RECTtoltwh(aRECT) (int)aRECT.left,(int)aRECT.top,(int)aRECT.right-aRECT.left,(int)aRECT.bottom-aRECT.top//将RECT转化为左上长宽
#define RECTtoltrb(aRECT) aRECT.left,aRECT.top,aRECT.right,aRECT.bottom//将RECT转化为左上右下
#define My_RoundRect(ahdc,aRC,width,height) (RoundRect(ahdc,aRC.left,aRC.top,aRC.right,aRC.bottom,width*RelSize,height*RelSize))//不用一个个输各个方向
#define My_DrawText(ahdc,lpchText,alpRC,format) (DrawText(ahdc,lpchText,_tcslen(lpchText),(alpRC),format))//输出字符串
#define DT_EVERYCENTER (DT_CENTER | DT_VCENTER | DT_SINGLELINE)//垂直和水平方向均居中
#define My_CreSelFont(ahdc,ahFont,nHeight,cWidth,cWeight) ahFont=My_CreateFont(ahdc,nHeight,cWidth,cWeight);SelectObject(ahdc,ahFont)//快捷修改Font
#define My_hSetTextColFont(ahdc,ahFont,nHeight,cWidth,cWeight,aColor) My_CreSelFont(ahdc,ahFont,nHeight,cWidth,cWeight);SetTextColor(ahdc,(aColor))//快捷修改字体Color和Font并设置
//#define My_hSetBrushPen(ahdc,ahBrush,BrushCol,ahPen,iStyle,cWidth,PenCol) ahBrush=CreateSolidBrush((BrushCol));SelectObject((ahdc),(ahBrush));ahPen=CreatePen(iStyle,(cWidth)*R,(PenCol));SelectObject(ahdc,ahPen)//快捷修改hBrush和hPen并设置(已乘R
#define RMathNum 4//标准格子数

// 全局变量:
int Math[MMyNum][MMxNum] = {};
int MRCWidth = 0;//单个物质格子宽
int MRCHeight = 0;
MyTimer aTimer; double PreEscSec = MAXEscSec, MinEscSec = MAXEscSec;//计时相关
LPARAM PreMouseIn;//鼠标位置
bool InView = false, InColFloat = false, InCovering = false;// ULONGLONG StopTime = 0;

RECT  StandardSizeRC; double RelSize = 1;//缩放窗口后的大小相对于标准大小的值，用于改变窗口后仍能适应大小
double RMath = 1;//调整数字横纵数量后相对于标准4*4大小的值，用于防止数字显示过密或过疏
RECT ClientMainRC;//绘图区大小

//可点击按键或一片区域的RC
Rect NumRC[MMyNum][MMxNum], TableRC, TimerRC, RecordRC, NewGameRC, MenuRC, ViewRC, StopRC, ColFloatRC, CoveringRC, toRecordRC;



HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名

// 此代码模块中包含的函数的前向声明:

void UpdateRC();
void RectSizeMul(LPRECT lpRC, double NulNum);//使RC整体保持中心不变放大缩小NulNum倍
bool isClickInRC(RECT RC, LPARAM lParam);//鼠标是否在某个区域
bool isClickInRC2(int left, int right, int top, int bottom, LPARAM lParam);//鼠标是否在某个区域
HFONT My_CreateFont(HDC hdc, int nHeight, int cWidth, int  cWeight = FW_BOLD);//设置字体,已自动*R校准,nHeight高度,cWidth宽度(可为0),cWeight粗细
RECT SpreadRC(int x, int y, int dx, int dy);//展开变成矩形
bool MoveMath();//判断是否能移动数字并移动，返回是否移动了数字
void RandMoveMath();//随机移动数字
bool isWin();//赢了
bool toRecordinFile(double minTime);//记录最佳时间
RECT aRECT;//中间的rect
RECT RecttoRECT(Rect aRect) {//Rect到RECT
    SetRect(&aRECT, aRect.GetLeft(), aRect.GetTop(), aRect.GetRight(), aRect.GetBottom());
    return aRECT;
}
LPRECT RecttoLPRECT(Rect aRect) {//Rect到pRECT
    SetRect((&aRECT), aRect.GetLeft(), aRect.GetTop(), aRect.GetRight(), aRect.GetBottom());
    return &aRECT;
}

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。
    
    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MATHGAME, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    // 初始化GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MATHGAME));

    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    // 关闭GDI+
    GdiplusShutdown(gdiplusToken);
    return (int) msg.wParam;
}




//
//  函数: MyRegisterClass()
//
//  目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MATHGAME));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MATHGAME);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   函数: InitInstance(HINSTANCE, int)
//
//   目标: 保存实例句柄并创建主窗口
//
//   注释:
//
//        在此函数中，我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目标: 处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//
//

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HDC hdcBuffer;
    static HBITMAP hbmBuffer;
    switch (message)
    {
    case WM_CREATE:
    {
        //获得系统参数
        int cxScreen = GetSystemMetrics(SM_CXSCREEN);
        int cyScreen = GetSystemMetrics(SM_CYSCREEN);
        int cyCaption = GetSystemMetrics(SM_CYCAPTION);
        int cxSizeFrame = GetSystemMetrics(SM_CXSIZEFRAME);
        int cySizeFrame = GetSystemMetrics(SM_CYSIZEFRAME);
        RECT WinRC;//默认大小
        WinRC.left = (cxScreen - OriginalClientMainRCcx) / 2;
        WinRC.top = (cyScreen - OriginalClientMainRCcy) / 2;
        WinRC.right = OriginalClientMainRCcx + 2 * cxSizeFrame + 9;
        WinRC.bottom = OriginalClientMainRCcy + cyCaption + 2 * cySizeFrame + 9;
        SetWindowPos(hWnd, NULL, WinRC.left, WinRC.top, WinRC.right, WinRC.bottom, SWP_NOOWNERZORDER);
        SetRect(&StandardSizeRC, 0, 0, OriginalClientMainRCcx, OriginalClientMainRCcy);
        //重置数字
        srand(GetTickCount64());
        for (int y = 0; y < MMyNum; y++) {
            for (int x = 0; x < MMxNum; x++) {
                Math[y][x] = (x + MMxNum * y + 1);
            }
        }
        Math[MMyNum - 1][MMxNum - 1] = NullMath;
        RandMoveMath();
        //确定合适大小
        RMath = RMathNum / (double)(max(MMxNum, MMyNum));
        //UpdateRC();
        GetClientRect(hWnd, &ClientMainRC);
        InvalidateRect(hWnd, &ClientMainRC, true);
        SetTimer(hWnd, DelayInvalidateTimer, 30, NULL);
        //PostMessage(hWnd, WM_SIZE, SIZE_RESTORED, ((ClientMainRC.bottom - ClientMainRC.top)) << 16 | (ClientMainRC.right - ClientMainRC.left));
    }
    break;
    case WM_SIZE:
    {
        ClientMainRC.right = LOWORD(lParam);
        ClientMainRC.bottom = HIWORD(lParam);
        if (StandardSizeRC.right > 0 && StandardSizeRC.bottom > 0) {//防止除数小于0
            double StandardRight = StandardSizeRC.right, StandardBottom = StandardSizeRC.bottom;
            RelSize = min(ClientMainRC.right / StandardRight, ClientMainRC.bottom / StandardBottom);
        }
        UpdateRC();
        if (hdcBuffer != NULL)
        {
            SelectObject(hdcBuffer, 0);
            DeleteObject(hbmBuffer);
            DeleteDC(hdcBuffer);
            hdcBuffer = NULL;
            hbmBuffer = NULL;
        }
    }
    break;
    case WM_LBUTTONDOWN:
        if (isClickInRC(RecttoRECT(TableRC), lParam) && (!aTimer.isStop())) {
            SetTimer(hWnd, GetMouseInRCTimer, 30, NULL);
            if (aTimer.Start()) {
                SetTimer(hWnd, InvalidateRectTimer, 30, NULL);
            }
        }
        break;
    case WM_LBUTTONUP:
    {
        Point MousePlace(LOWORD(lParam), HIWORD(lParam));
        if (isClickInRC(RecttoRECT(NewGameRC), lParam)) {
            KillTimer(hWnd, InvalidateRectTimer);
            KillTimer(hWnd, GetMouseInRCTimer);
            aTimer.Kill();
            RandMoveMath();//重置游戏
            InvalidateRect(hWnd, &ClientMainRC, true);
        }
        else if (ViewRC.Contains(MousePlace)) {
            if (aTimer.Stop()) {
                KillTimer(hWnd, InvalidateRectTimer);
                KillTimer(hWnd, GetMouseInRCTimer);
            }
            else {
                aTimer.Continue();
                if (aTimer.isStart())
                    SetTimer(hWnd, InvalidateRectTimer, 30, NULL);
            }
            InView = (!InView);
        }
        else if (StopRC.Contains(MousePlace)) {
            if (aTimer.Stop()) {
                KillTimer(hWnd, InvalidateRectTimer);
                KillTimer(hWnd, GetMouseInRCTimer);
            }
            else if (aTimer.Continue()) {
                if (aTimer.isStart())
                    SetTimer(hWnd, InvalidateRectTimer, 30, NULL);
                InView = false;
            }
        }
        else if (ColFloatRC.Contains(MousePlace))
            InColFloat = (!InColFloat);
        else if (CoveringRC.Contains(MousePlace))
            InCovering = (!InCovering);
        else if (toRecordRC.Contains(MousePlace))
            toRecordinFile(MinEscSec);
        InvalidateRect(hWnd, &ClientMainRC, true);
        KillTimer(hWnd, GetMouseInRCTimer);
        break;
    }
    case WM_MOUSEMOVE:
        PreMouseIn = lParam;
        break;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        TCHAR XNAME[100];
        if (hdcBuffer == NULL)
        {
            hdcBuffer = CreateCompatibleDC(hdc);
            hbmBuffer = CreateCompatibleBitmap(hdc, ClientMainRC.right, ClientMainRC.bottom);
        }
        HBITMAP hbmOldBuffer = (HBITMAP)SelectObject(hdcBuffer, hbmBuffer);
        SetBkMode(hdcBuffer, NULL);

        // TODO: 在此处添加使用 hdc 的任何绘图代码...

        Rect MainRC(RECTtoltwh(ClientMainRC));
        // 创建Graphics对象
        Graphics graphics(hdcBuffer);

        SolidBrush Brush_Bkg(Color(200, 200, 200)); //背景色刷子
        graphics.FillRectangle(&Brush_Bkg, MainRC);
        {
            HFONT hFont;
            My_hSetTextColFont(hdcBuffer, hFont, 20, 0, FW_BOLD, Col_FontNum);//20粗数字色字体
            sprintf_s(XNAME, 100, _T("%.3fs"), aTimer.GetSec());
            My_DrawText(hdcBuffer, XNAME, RecttoLPRECT(TimerRC), DT_LEFT);
            int Used = 0;
            if (MinEscSec < MAXEscSec)
                Used = sprintf_s(XNAME, 99, _T("最%.3fs"), MinEscSec);
            else
                Used = sprintf_s(XNAME, 99, _T("最Ns"));
            if (PreEscSec < MAXEscSec)
                sprintf_s(XNAME + Used, 99 - Used, _T("  p:%.3fs"), PreEscSec);
            else
                sprintf_s(XNAME + Used, 99 - Used, _T("  p:Ns"));
            My_DrawText(hdcBuffer, XNAME, RecttoLPRECT(RecordRC), DT_RIGHT);
            DeleteObject(hFont);
        }
        SolidBrush Brush_MenuBkg(Color(140, 150, 150)); //菜单背景色刷子
        SolidBrush Brush_MenuGreen(Color(150, 225, 165)); //绿菜单色刷子
        SolidBrush Brush_Menu(Color(210, 165, 150)); //菜单色刷子
        graphics.FillRectangle(&Brush_MenuBkg, MenuRC);
        graphics.FillRectangle(&Brush_Menu, NewGameRC);
        graphics.FillRectangle(InView ? &Brush_MenuGreen : &Brush_Menu, ViewRC);
        graphics.FillRectangle(aTimer.isStop() ? &Brush_MenuGreen : &Brush_Menu, StopRC);
        graphics.FillRectangle(InColFloat ? &Brush_MenuGreen : &Brush_Menu, ColFloatRC);
        graphics.FillRectangle(InCovering ? &Brush_MenuGreen : &Brush_Menu, CoveringRC);
        graphics.FillRectangle(&Brush_Menu, toRecordRC);
        {
            HFONT hFont;
            My_hSetTextColFont(hdcBuffer, hFont, 20, 0, FW_EXTRABOLD, Col_FontMenu);//20粗额外粗菜单色字体
            My_DrawText(hdcBuffer, _T("new"), RecttoLPRECT(NewGameRC), DT_EVERYCENTER);
            My_DrawText(hdcBuffer, _T("咋搞"), RecttoLPRECT(ViewRC), DT_EVERYCENTER);
            if (!aTimer.isStop())
                My_DrawText(hdcBuffer, _T(" | | "), RecttoLPRECT(StopRC), DT_EVERYCENTER);
            else
                My_DrawText(hdcBuffer, _T("go"), RecttoLPRECT(StopRC), DT_EVERYCENTER);
            My_DrawText(hdcBuffer, _T("渐变"), RecttoLPRECT(ColFloatRC), DT_EVERYCENTER);
            My_DrawText(hdcBuffer, _T("隐藏"), RecttoLPRECT(CoveringRC), DT_EVERYCENTER);
            My_DrawText(hdcBuffer, _T("记录"), RecttoLPRECT(toRecordRC), DT_EVERYCENTER);
            DeleteObject(hFont);
        }
        //数字部分：
        SolidBrush Brush_NumBkgNULL(Color(250, 250, 250));//数字空白处刷子
        SolidBrush Brush_NumBkg(Color(220, 220, 220));//数字处刷子
        SolidBrush Brush_Table(Color(200, 130, 150)); //数字表色刷子
        graphics.FillRectangle(&Brush_Table, TableRC);
        {
            HFONT hFont;
            My_hSetTextColFont(hdcBuffer, hFont, 55 * RMath, 0, FW_BOLD, Col_FontNum);//55粗数字色字体
            for (int y = 0; y < MMyNum; y++) {
                for (int x = 0; x < MMxNum; x++) {
                    if ((Math[y][x] == MMxNum * MMyNum) && (InView ? InCovering : true)) {//非特殊模式且该格为空（即最大数+1）
                        graphics.FillRectangle(&Brush_NumBkgNULL, NumRC[y][x]);
                    }
                    else if (InColFloat) {//渐变模式
                        int xPlace, yPlace;
                        if (!(InView&&(!InCovering))) {//显示该格数字的颜色：+不显示+不隐藏 or +隐藏+显示 or +不显示+隐藏
                            xPlace = (Math[y][x] - 1) % MMyNum + 1;
                            yPlace = (Math[y][x] - xPlace) / 4 + 1;
                        }
                        else {//显示完成情况下的颜色：+显示+不隐藏
                            xPlace = x + 1;
                            yPlace = y + 1;
                        }
                        Color NumCol;
                        Brush_NumBkg.GetColor(&NumCol);
                        SolidBrush abrush(Color(NumCol.GetR() - Col_NumBkgColFloat.GetR() * (xPlace - 1), NumCol.GetG(), NumCol.GetB() - Col_NumBkgColFloat.GetB() * (yPlace - 1)));
                        graphics.FillRectangle(&abrush, NumRC[y][x]);
                    }
                    else//通常情况
                        graphics.FillRectangle(&Brush_NumBkg, NumRC[y][x]);
                    if (!(InView ^ InCovering)) {//两个同时开启或关闭时
                        if (Math[y][x] != NullMath) {
                            sprintf_s(XNAME, 10, _T("%d"), Math[y][x]);
                            My_DrawText(hdcBuffer, XNAME, RecttoLPRECT(NumRC[y][x]), DT_EVERYCENTER);
                        }
                    }
                    else if (InView) {
                        sprintf_s(XNAME, 10, _T("%d"), (x + MMxNum * y + 1));
                        My_DrawText(hdcBuffer, XNAME, RecttoLPRECT(NumRC[y][x]), DT_EVERYCENTER);
                    }
                }
            }
            DeleteObject(hFont);
        }

        if (isWin()) {
            HFONT hFont;
            My_hSetTextColFont(hdcBuffer, hFont, 25, 0, FW_EXTRABOLD, Col_FontWin);//25粗额外粗胜利色字体
            RECT aWinRC = SpreadRC((ClientMainRC.right - ClientMainRC.left) / 2, (ClientMainRC.bottom - ClientMainRC.top) / 2, R * 150, R * 150);
            My_DrawText(hdcBuffer, _T("You Win!"), &aWinRC, DT_EVERYCENTER);
            DeleteObject(hFont);
        } 

        

        BitBlt(hdc, ClientMainRC.left, ClientMainRC.top, ClientMainRC.right - ClientMainRC.left, ClientMainRC.bottom - ClientMainRC.top, hdcBuffer, 0, 0, SRCCOPY);
        SelectObject(hdcBuffer, hbmOldBuffer);
        EndPaint(hWnd, &ps);
    }
        break;

    case WM_TIMER:
        switch (wParam)
        {
        case InvalidateRectTimer:
            InvalidateRect(hWnd, RecttoLPRECT(TimerRC), true);
            break;
        case GetMouseInRCTimer:
            if (MoveMath()) {
                InvalidateRect(hWnd, RecttoLPRECT(TableRC), true);
                if (isWin()) {
                    if (aTimer.GetSec() < MinEscSec) {//更新最高记录
                        MinEscSec = aTimer.GetSec();
                    }
                    PreEscSec = aTimer.GetSec();//更新上次记录
                    KillTimer(hWnd, InvalidateRectTimer);
                    KillTimer(hWnd, GetMouseInRCTimer);
                    aTimer.Kill();
                    UpdateWindow(hWnd);//立即刷新屏幕
                    Sleep(1500);
                    RandMoveMath();//重置游戏
                    InvalidateRect(hWnd, &ClientMainRC, true);
                }
            }
            break;
        case DelayInvalidateTimer:
            InvalidateRect(hWnd, &ClientMainRC, true);
            KillTimer(hWnd, DelayInvalidateTimer);
            break;
        default:
            break;
        }
        break;
    case WM_DESTROY:
        if (hdcBuffer != NULL)
        {
            SelectObject(hdcBuffer, NULL);
            DeleteObject(hbmBuffer);
            DeleteDC(hdcBuffer);
        }
        for (int i = 1; i <= TimerNum; i++) {
            KillTimer(hWnd, i);
        }
        toRecordinFile(MinEscSec);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

void UpdateRC() {
    //标准值0，0，500，480，My_SetRect自动乘以标准大小RelSize
    //My_SetRect(ClientMainRC, 0, 0, OriginalClientMainRCcx, OriginalClientMainRCcy);
    //RECTNAME          LEFT        TOP         WIDTH       HEIGHT
    TableRC = Rect(     0 * R,      30 * R,     450 * R,    450 * R);
    TimerRC = Rect(     0 * R,      0 * R,      130 * R,    30 * R);
    RecordRC = Rect(    130 * R,    0 * R,      320 * R,    30 * R);
    MenuRC = Rect(      450 * R,    0 * R,      60 * R,     480 * R);
    NewGameRC = Rect(   455 * R,    50 * R,     50 * R,     30 * R);
    ViewRC = Rect(      455 * R,    100 * R,    50 * R,     30 * R);
    StopRC = Rect(      455 * R,    150 * R,    50 * R,     30 * R);
    ColFloatRC = Rect(  455 * R,    200 * R,    50 * R,     30 * R);
    CoveringRC = Rect(  455 * R,    250 * R,    50 * R,     30 * R);
    toRecordRC = Rect(  455 * R,    300 * R,    50 * R,     30 * R);
    
    MRCWidth = (int)((TableRC.GetRight() - TableRC.GetLeft() - (MMxNum - 1) * MMSpace) / MMxNum);//单个物质格子宽
    MRCHeight = (int)((TableRC.GetBottom() - TableRC.GetTop() - (MMyNum - 1) * MMSpace) / MMyNum);//单个物质格子高
    for (int y = 0; y < MMyNum; y++) {
        for (int x = 0; x < MMxNum; x++) {
            NumRC[y][x]=Rect(
                (int)(TableRC.GetLeft() + x * MMSpace + x * MRCWidth),//left
                (int)(TableRC.GetTop() + y * MMSpace + y * MRCHeight),//top
                (int)(MRCWidth),//width
                (int)(MRCHeight));//height
        }
    }

}


void RectSizeMul(LPRECT lpRC, double NulNum) {
    int xDelta = (int)((1 - NulNum) * abs(lpRC->right - lpRC->left) / 2);
    int yDelta = (int)((1 - NulNum) * abs(lpRC->bottom - lpRC->top) / 2);
    lpRC->left += xDelta;
    lpRC->right -= xDelta;
    lpRC->top += yDelta;
    lpRC->bottom -= yDelta;
}

bool isClickInRC(RECT RC, LPARAM lParam) {
    int Mx = LOWORD(lParam);
    int My = HIWORD(lParam);
    if (RC.left <= Mx && RC.right >= Mx && RC.top <= My && RC.bottom >= My)
        return true;
    else
        return false;
}

bool isClickInRC2(int left, int right, int top, int bottom, LPARAM lParam) {
    int Mx = LOWORD(lParam);
    int My = HIWORD(lParam);
    if ((R * left) <= Mx && (R * right) >= Mx && (R * top) <= My && (R * bottom) >= My)
        return true;
    else
        return false;
}


HFONT My_CreateFont(HDC hdc, int nHeight, int cWidth, int  cWeight) {//设置字体,已自动*R校准,nHeight高度,cWidth宽度(可为0),cWeight粗细
    nHeight = -MulDiv(nHeight * R, GetDeviceCaps(hdc, LOGPIXELSY), 72);
    return(CreateFontA(nHeight, R * cWidth, 0, 0, R * cWeight, 0, 0, 0, 0, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, FF_SCRIPT, NULL));
}

RECT SpreadRC(int x, int y, int dx, int dy) {
    RECT reRC;
    SetRect(&reRC, x - dx / 2, y - dy / 2, x + dx / 2, y + dy / 2);
    return reRC;
}

bool MoveMath() {
    int CurNullMathx = MMxNum, CurNullMathy = MMyNum, WillNullMathx = MMxNum, WillNullMathy = MMyNum;//现在为空和将来应该为空的位置，若未检索到Null则为默认的MMxNum
    int MathDeltax = 0, MathDeltay = 0;
    int MathSwap = NullMath;

    for (int y = 0; y < MMyNum; y++) {//获取当前或将可能为空的数字
        for (int x = 0; x < MMxNum; x++) {
            if (Math[y][x] == NullMath) {
                CurNullMathy = y; CurNullMathx = x;
            }
            if (isClickInRC((RecttoRECT(NumRC[y][x])), PreMouseIn) && Math[y][x] != NullMath) {//若鼠标所在格子数字不为空
                WillNullMathy = y; WillNullMathx = x;
            }
        }
    }

    if (WillNullMathy == MMyNum || WillNullMathx == MMxNum)//若鼠标不在格子内
        return false;
    else if ((CurNullMathx == WillNullMathx) ^ (CurNullMathy == WillNullMathy)) {//必须保证两在同一行或列且不能完全相同才能移动
        MathDeltay = WillNullMathy - CurNullMathy;
        MathDeltax = WillNullMathx - CurNullMathx;
        if (MathDeltax > 0) {//下面四个分别是向四个不同方向移动的情况，右
            for (int x = 0; x < MathDeltax; x++) {
                MathSwap = Math[CurNullMathy][CurNullMathx];//交换
                Math[CurNullMathy][CurNullMathx] = Math[CurNullMathy][CurNullMathx + 1];
                Math[CurNullMathy][CurNullMathx + 1] = MathSwap;
                CurNullMathx++;
            }
        }
        else if (MathDeltax < 0) {//左
            for (int x = 0; x > MathDeltax; x--) {
                MathSwap = Math[CurNullMathy][CurNullMathx];
                Math[CurNullMathy][CurNullMathx] = Math[CurNullMathy][CurNullMathx - 1];
                Math[CurNullMathy][CurNullMathx - 1] = MathSwap;
                CurNullMathx--;
            }
        }
        else if (MathDeltay > 0) {//下
            for (int y = 0; y < MathDeltay; y++) {
                MathSwap = Math[CurNullMathy][CurNullMathx];
                Math[CurNullMathy][CurNullMathx] = Math[CurNullMathy + 1][CurNullMathx];
                Math[CurNullMathy + 1][CurNullMathx] = MathSwap;
                CurNullMathy++;
            }
        }
        else{//上
            for (int y = 0; y > MathDeltay; y--) {
                MathSwap = Math[CurNullMathy][CurNullMathx];
                Math[CurNullMathy][CurNullMathx] = Math[CurNullMathy - 1][CurNullMathx];
                Math[CurNullMathy - 1][CurNullMathx] = MathSwap;
                CurNullMathy--;
            }
        }
        return true;
    }
    return false;
}

void RandMoveMath() {
    int MathSwap = 0;
    int NullMathx = 0, NullMathy = 0;
    for (int y = 0; y < MMyNum; y++) {//获取为空的数字
        for (int x = 0; x < MMxNum; x++) {
            if (Math[y][x] == NullMath) {
                NullMathy = y; NullMathx = x;
            }
        }
    }
    Again:
    for (int a = 0; a < RandMathTimes; a++) {
        switch ((rand() % 2))
        {
        case 0://右
            if (NullMathx != (MMxNum - 1)) {
                MathSwap = Math[NullMathy][NullMathx];
                Math[NullMathy][NullMathx] = Math[NullMathy][NullMathx + 1];
                Math[NullMathy][NullMathx + 1] = MathSwap;
                NullMathx++;
            }
            break;
        default://左
            if (NullMathx != 0) {
                MathSwap = Math[NullMathy][NullMathx];
                Math[NullMathy][NullMathx] = Math[NullMathy][NullMathx - 1];
                Math[NullMathy][NullMathx - 1] = MathSwap;
                NullMathx--;
            }
            break;
        }
        switch ((rand() % 2))
        {
        case 0://下
            if (NullMathy != (MMyNum - 1)) {
                MathSwap = Math[NullMathy][NullMathx];
                Math[NullMathy][NullMathx] = Math[NullMathy + 1][NullMathx];
                Math[NullMathy + 1][NullMathx] = MathSwap;
                NullMathy++;
            }
            break;
        default://上
            if (NullMathy != 0) {
                MathSwap = Math[NullMathy][NullMathx];
                Math[NullMathy][NullMathx] = Math[NullMathy - 1][NullMathx];
                Math[NullMathy - 1][NullMathx] = MathSwap;
                NullMathy--;
            }
            break;
        }
    }
    if (isWin())
        goto Again;//用于防止生成一开始就赢的局
}

bool isWin(){
    for (int x = 0; x < MMxNum; x++) {
        for (int y = 0; y < MMyNum; y++) {
            if (Math[y][x] != (x + MMxNum * y + 1))
                return false;
        }
    }
    return true;
}

bool toRecordinFile(double minTime) {
    FILE* FRecord;
    static double preminTime;
    if (minTime != MAXEscSec && (preminTime != minTime)) {
        preminTime = minTime;
        CreateDirectory(_T("record"), NULL);//创建文件夹
        TCHAR achar[100];
        sprintf_s(achar, 100, _T("record\\%d_%d.txt"), MMxNum, MMyNum);
        if (fopen_s(&FRecord, (PCHAR)achar, "at") == NULL) {
            TCHAR achar2[100];
            SYSTEMTIME Stmt;
            GetLocalTime(&Stmt);
            sprintf_s(achar2, 100, _T("%d/%d/%d week%d %d:%d:%d record：%.3fs\n·"), Stmt.wYear, Stmt.wMonth, Stmt.wDay, Stmt.wDayOfWeek == 0 ? 7 : Stmt.wDayOfWeek, Stmt.wHour, Stmt.wMinute, Stmt.wSecond, minTime);
            fwrite(achar2, _tcsclen(achar2), 1, FRecord);
            fclose(FRecord);
            return true;
        }
        else
            return false;
    }
    else
        return false;
}